/*
Copyright 2021 LuJun

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal 
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is furnished 
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in 
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/// <reference path="../base/lcc.ts" />

namespace lcc {

const TAG = "lcc/worker/worker.ts";

/**
 * @zh
 * 工作者状态
 */
export enum WorkerState {
    
    /**
     * @zh
     * 已停止，表示worker未启动前，或停止后
     */
    STOPPED,

    /**
     * @zh
     * 启动中，表示worker正在启动
     */
    STARTING,

    /**
     * @zh
     * 运行中，表示worker启动完成，正在运行中
     */
    RUNNING,

    /**
     * @zh
     * 软停止中，表示worker正在停止，但是如果有调用会重新进入运行状态，否则等待工作全部完成会进入停止中状态
     */
    SOFT_STOPPING,

    /**
     * @zh
     * 停止中，表示worker正在停止，停止后会切换到STOPPED状态
     */
    STOPPING,
}

/**
 * @zh
 * Worker调用错误
 */
export enum WorkerCallError {
    /**
     * @zh
     * 调用异常
     */
    CALL_EXCEPTION = 1,

    /**
     * @zh
     * 所属组不匹配
     */
    GROUP_NOT_MATCH,

    /**
     * @zh
     * 所属组未工作
     */
    GROUP_NOT_WORK,

    /**
     * @zh
     * 工作者未找到
     */
    WORKER_NOT_FOUND,

    /**
     * @zh
     * 工作者未运行
     */
    WORKER_NOT_RUNNING,

    /**
     * @zh
     * 函数未找到
     */
    FUNCTION_NOT_FOUND,

    /**
     * @zh
     * 工作者不能调用
     */
    WORKER_NOT_CALLABLE,
}

/**
 * @zh
 * Worker调用结果
 */
export interface WorkerCallResult {
    /**
     * @zh
     * 调用错误
     */
    error?:WorkerCallError;

    /**
     * @zh
     * 调用返回值
     */
    return?:any;
}

/**
 * @zh
 * 工作者基类<br/>
 * 用户继承这个基类，实现自己的工作逻辑<br/>
 * 工作者作为基本的逻辑单元，可以使用它搭建复杂的服务器程序逻辑。<br/>
 * 为什么会有工作者这种基本逻辑单元？<br/>
 * 因为如果服务器使用typescript开发，并且使用了大量await/async异步语法，会遇到一个比较麻烦的问题：<br/>
 * 如果一个对象需要销毁，但是对象里面异步函数还没有返回；如果对象立即销毁，那么后面异步函数可能会崩溃。<br/>
 * 当然我们也可以直接使用try/catch捕捉崩溃异常，但是我们很难纠正这个崩溃问题。<br/>
 * 所以，最好的解决办法：在对象销毁的时候，保证异步函数全部返回。<br/>
 * 实现也很简单，只要在每个异步函数中添加计数就可以了，如果计数为0就表示没有运行中的异步函数<br/>
 * 当然如果我们在自己的异步函数中频繁添加这样的操作，会使程序易读性变差，而且也可能某个函数忘记添加使程序稳定性变差。<br/>
 * 工作者就这样应运而生，它内部封装了异步函数引用计数，语法上通过call平行调用其他工作者逻辑。<br/>
 * 为了提高效率，工作者可以在不同的工作组中运行，工作组可以是主线程，也可以是工作线程或者进程。<br/>
 * 特殊事件：<br/>
 *      `__onWorkCall`,()=>{} : 表示存在工作调用，比如通过createWork创建的回调函数，或者其他工作者调用此工作者<br/>
 *      `__state-${状态}`,()=>{} : 表示工作者的状态变为指定${状态}<br/>
 *      `__state`,(state)=>{} : 表示工作者的状态变为state<br/>
 */
export class WorkerBase extends cocos.event.EventTarget {

    /**
     * @zh
     * 工作者所属分组
     */
    private _workerGroup:WorkerGroup = null;
    
    /**
     * @zh
     * 工作者优先级
     */
    private _workerPriority:number = 0;

    /**
     * @zh
     * 工作者名称
     */
    private _workerName:string = null;

    /**
     * @zh
     * 工作者实例
     */
    private _instanceName:string = null;

    /**
     * @zh
     * 工作者工作数量
     */
    private _works:number = 0;

    /**
     * @zh
     * 工作者状态
     */
    private _state:WorkerState = WorkerState.STOPPED;

    /**
     * @zh
     * 更新延迟
     */
    private _updateDelay:boolean = false;

    /**
     * @zh
     * 是否可以被调用
     */
    private _callable:boolean = true;

    /**
     * @zh
     * 检测是否可以被调用
     */
    public isCallable(){
        return this._callable;
    }

    /**
     * @zh
     * 设置是否可以被调用
     */
    public setCallable(callable:boolean){
        this._callable = callable;
    }

    /**
     * @zh
     * 获得分组
     * 
     * @return 工作者分组
     */
    public getGroup() {
        return this._workerGroup;
    }

    /**
     * @zh
     * 设置分组<br/>
     * 由框架调用
     * 
     * @internal
     * 
     * @param group - 分组名称
     */
    public _setGroup(group:WorkerGroup){
        this._workerGroup = group;
    }
    
    /**
     * @zh
     * 获得工作者优先级
     */
    public getPriority() {
        return this._workerPriority;
    }

    /**
     * @zh
     * 设置工作者优先级
     * 
     * @param priority - 优先级
     */
    public setPriority(priority:number) {
        this._workerPriority = priority;
    }

    /**
     * @zh
     * 获得名称
     * 
     * @return 工作者名称
     */
    public getWorkerName(){
        return this._workerName;
    }

    /**
     * @zh
     * 设置名称<br/>
     * 由框架调用
     * 
     * @internal
     * 
     * @param workerName - 名称
     */
    public _setWorkerName(workerName:string){
        this._workerName = workerName;
    }

    /**
     * @zh
     * 获得实例
     * 
     * @return 工作者实例
     */
    public getInstanceName(){
        return this._instanceName;
    }

    /**
     * @zh
     * 设置实例<br/>
     * 由框架调用
     * 
     * @internal
     * 
     * @param instance - 实例
     */
    public _setInstanceName(instanceName:string){
        this._instanceName = instanceName;
    }

    /**
     * @zh
     * 获得工作者状态
     */
    public getState(){
        return this._state;
    }

    /**
     * @zh
     * 设置工作者状态
     * 
     * @param state 状态
     */
    public _setState(state:WorkerState){
        if(state != this._state){
            this._state = state;
            if(state == WorkerState.RUNNING){
                this._updateDelay = true;
            }
            this.emit(`__state-${state}`);
            this.emit(`__state`, state);
        }
    }
    
    /**
     * @zh
     * 直到工作者状态
     * 
     * @param state 状态
     */
    public async untilState(state:WorkerState){
        return new Promise((resolve)=>{ 
            if(this._state == state){
                resolve(null);
            }else{
                this.once(`__state-${state}`, resolve); 
            }
        });
    }
    
    /**
     * @zh
     * 获得工作计数
     */
    public getWorks(){
        return this._works;
    }
    
    /**
     * @zh
     * 工作计数增加
     */
    public _addWork() {
        this._works ++;
    }

    /**
     * @zh
     * 工作计数减少
     */
    public _decWork() {
        this._works --;
        if(this._works <= 0){
            if(this._state == WorkerState.SOFT_STOPPING){
                this._setState(WorkerState.STOPPING);
            }
            if(this._state == WorkerState.STOPPING){
                this.emit('__STOPPING');
            }
        }
    }

    /**
     * @zh
     * 判断是否正在运行
     * 
     * @return 是否正在运行
     */
    public isRunning(){
        return this._state == WorkerState.RUNNING;
    }

    /**
     * @zh
     * 创建指定回调函数的工作封装<br/>
     * 如果外部对象需要使用到当前工作者的函数回调，那么需要把指定回调函数使用当前函数封装。
     * 
     * @param workFunction - 工作函数
     * @param defFunReturn - 工作函数默认返回，如果出现异常返回该值
     */
    protected createWork<R = any>(workFunction:(...args:any[])=>Promise<R>, defFunReturn:R = null){
        return async (...args:any[])=>{
            if(this._state == WorkerState.SOFT_STOPPING){
                this._setState(WorkerState.RUNNING);
            }
            let result = defFunReturn;
            if(this.isRunning()){
                this._addWork();
                try {
                    this.emit('__onWorkCall');
                    result = await workFunction(...args);
                }catch(e){
                    lcc.logMgr.error(TAG, e);
                }
                this._decWork();
            }else{
                logMgr.warn(TAG,`createWork : worker ${this._workerGroup.getName()}-${this._workerName} is not running`);
            }
            return result;
        };
    }
    
    /**
     * @zh
     * 调用工作者<br/>
     * 如果只指定工作者名称，则表示调用当前组中的工作者
     * 
     * @param workerIDorMeta - 工作者标识或者工作者元数据。如果为字符串，则表示工作者名称，所属工作组默认为当前工作组
     * 
     * @param type - 调用类型
     * @param args - 调用参数
     */
    protected async callWorker(workerIDorMeta:string|WorkerIdentifier|WorkerMeta, type:string, ...args:any[]){
        let workerMeta = optionWorkerMeta(workerIDorMeta);
        if(this._workerGroup.containWorkerName(workerMeta.id.workerName)){
            return await this._workerGroup.callWorker(workerMeta, type, ...args);
        }else{
            return await workerMgr.callWorker(workerMeta, type, ...args);
        }
    }

    /**
     * @zh
     * 通知当前工作组
     * 
     * @param type 调用类型
     * @param args 调用参数
     */
    protected async notifyGroup(type:string, ...args:any[]){
        await this._workerGroup.notifyAll(type, ...args);
    }
    
    /**
     * @zh
     * *内置功能，不要覆盖*<br/>
     * 判断是否为主线程工作者<br>
     */
    protected isMasterWorker(){
        return IS_MASTER;
    }

    /**
     * @zh
     * *内置功能，不要覆盖*<br/>
     * 判断是否为子线程工作者<br>
     */
    protected isThreadWorker(){
        return IS_WORKER_THREAD;
    }

    /**
     * @zh
     * *内置功能，不要覆盖*<br/>
     * 判断是否为子进程工作者<br>
     */
    protected isProcessWorker(){
        return IS_WORKER_PROCESS;
    }

    /**
     * @zh
     * 更新函数
     */
    public async _update(dt:number){
        if(this._updateDelay){
            this._updateDelay = false;
            return;
        }
        this._addWork();

        // 自定义更新
        await this.onUpdate(dt);

        this._decWork();
    }
    
    /**
     * @zh
     * 当开始工作者<br/>
     * 用户应该重写该函数，以实现自己的开始工作逻辑
     */
    public async onStart(){}

    /**
     * @zh
     * 当停止工作者<br/>
     * 用户应该重写该函数，以实现自己的停止工作者逻辑
     */
    public async onStop(){}

    /**
     * @zh
     * 如果工作者是运行状态，那么会重复调用更新<br/>
     * 如果需要，用户应该重写该函数
     * 
     * @param dt - 间隔时间
     */
    public async onUpdate(dt:number){}

}

}
